﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using System.ComponentModel;
using System.Drawing;
using Microsoft.DirectX;
using System.Runtime.InteropServices;
using System.Windows.Input;
using DX.engine;

namespace DX
{
    public class WinForm : Form
    {
        public bool EnableCulling = true;
        public bool EnableWireframe = true;
        public bool EnableLighting = true;
        private Microsoft.DirectX.Direct3D.Device device;
        private Microsoft.DirectX.DirectInput.Device keyb;
        private Microsoft.DirectX.DirectInput.Device mouse;
        private Container components = null;

        private CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[3];
        private VertexBuffer vb;
        private IndexBuffer ib;
        private Mesh meshTerrain;
        private short[] indices;
        Terrain terrain;

        private Engine _Engine;

        public WinForm()
        {
            InitializeComponent();
            InitializeWindow();
            Engine.Run(this);
        }

        private void InitializeComponent()
        {
            Log("Initializing application.");
            this.components = new Container();
            this.Size = new System.Drawing.Size(800, 600);
            this.Text = "DirectX Tutorial";
        }

        private void InitializeWindow()
        {
            //Eliminates flickering on repaint by declaring that all painting is done by Windows Messaging
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        /// <summary>
        /// Initializes our hardware device for rendering.
        /// </summary>
        private void InitializeDevice()
        {
            Log("Initializing Device...");
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.MultiSample = MultiSampleType.TwoSamples;

            device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

            Log("Device Initialization successful!");
        }



        private void InitializeInput()
        {
            Log("Initializing Input...");
            keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            keyb.Acquire();

            mouse = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Mouse);
            mouse.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            mouse.Acquire();
            Log("Input Initialization successful!");
        }

        private void ResetScene()
        {
            CameraPositioning();
            CreateTerrain();
            VertexDeclaration();
            IndicesDeclaration();
            CreateMesh();
        }

        private void CameraPositioning()
        {
            Log("Positioning camera...");
            device.VertexFormat = CustomVertex.PositionColored.Format;
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 500f);
            device.Transform.View = Matrix.LookAtLH(new Vector3(0, -40, 100), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            device.RenderState.Lighting = EnableLighting;
            if (EnableLighting)
            {
                //Define our lights
                device.Lights[0].Type = LightType.Directional;
                device.Lights[0].Diffuse = Color.White;
                device.Lights[0].Direction = new Vector3(-0.5f, 0, -1f);
                device.Lights[0].Enabled = true;
            }
            if (!EnableCulling)
                device.RenderState.CullMode = Cull.None;
            else
                device.RenderState.CullMode = Cull.CounterClockwise;
            if (EnableWireframe)
                device.RenderState.FillMode = FillMode.WireFrame;
            else
                device.RenderState.FillMode = FillMode.Solid;
            Log("Finished positioning camera.");
        }

        private void CreateTerrain()
        {
            terrain = new Terrain("../../images/heightmap.bmp");
        }

        private void VertexDeclaration()
        {
            vb = new VertexBuffer(typeof(CustomVertex.PositionColored), terrain.WIDTH * terrain.HEIGHT, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
            vertices = terrain.GetVertices();
            vb.SetData(vertices, 0, LockFlags.None);
        }

        private void IndicesDeclaration()
        {
            indices = terrain.GetIndices();
            ib = new IndexBuffer(typeof(short), (terrain.WIDTH - 1) * (terrain.HEIGHT - 1) * 6, device, Usage.WriteOnly, Pool.Default);
            ib.SetData(indices, 0, LockFlags.None);
        }

        private void CreateMesh()
        {
            meshTerrain = new Mesh((terrain.WIDTH - 1) * (terrain.HEIGHT - 1) * 2, terrain.WIDTH * terrain.HEIGHT, MeshFlags.Managed, CustomVertex.PositionColored.Format, device);
            meshTerrain.SetVertexBufferData(vertices, LockFlags.None);
            meshTerrain.SetIndexBufferData(indices, LockFlags.None);

            int[] adjac = new int[meshTerrain.NumberFaces * 3];
            meshTerrain.GenerateAdjacency(0.5f, adjac);
            meshTerrain.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adjac);

            //Redefine as PNC and compute normals
            meshTerrain = meshTerrain.Clone(meshTerrain.Options.Value, CustomVertex.PositionNormalColored.Format, device);
            meshTerrain.ComputeNormals();
        }

        protected override void OnResizeEnd(EventArgs e)
        {
            base.OnResize(e);
            if (device != null)
                ResetScene();
        }


        protected override void OnPaint(PaintEventArgs e)
        {
            ////Perform Input
            //Input();

            ////Clear scene
            //device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);

            ////Render scene
            //device.BeginScene();
            ////device.SetStreamSource(0, vb, 0);
            ////device.Indices = ib;
            //device.Transform.World = Matrix.Translation(-terrain.WIDTH / 2, -terrain.HEIGHT / 2, 0);
            //int numSubSets = meshTerrain.GetAttributeTable().Length;
            //for (int i = 0; i < numSubSets; ++i)
            //{
            //    meshTerrain.DrawSubset(i);
            //}
            ////device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);   
            
            //device.EndScene();

            //device.Present();
            //this.Invalidate();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                    components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void Input()
        {
            MouseState m = mouse.CurrentMouseState;
            this.Text = string.Format("DirectX Tutorial + X:{0}, Y:{1}, Z:{2}", m.X, m.Y, m.Z);
            byte[] mButtons = m.GetMouseButtons();

            KeyboardState keys = keyb.GetCurrentKeyboardState();
            if (keys[Key.Escape])
            {
                Exit();
                return;
            }


            if (keys[Key.LeftControl] && keys[Key.W])
            {
                EnableWireframe = !EnableWireframe;
                ResetScene();
            }
        }

        private void Exit()
        {
            Log("Exiting!");
            Application.Exit();
        }

        public static void Log(string msg)
        {
            System.Diagnostics.Debug.WriteLine(DateTime.Now.ToShortTimeString() + ": " + msg);
        }
    }
}
